package com.jacob.androidframe.util;

import java.lang.reflect.Method;

import com.jacob.androidframe.BuildConfig;

import android.annotation.TargetApi;
import android.app.Activity;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;

/**
 * Android全版本通用全屏工具。
 * 
 * 使用注意事项：
 * 1.在V1至V18上使用时，应该尽量避免使用FLAG_HIDE_NAVIGATION_BAR。
 * 因为在V19之前，隐藏NavigationBar之后，Activity就无法得到点击事件，这也会导致toggle(long)方法无法正常工作。
 * 
 * 2.在V19上，可以使用FLAG_HIDE_NAVIGATION_BAR_V19，
 * 但应该和FLAG_IMMERSIVE或FLAG_IMMERSIVE_STICKY同时使用，
 * 这样才能让Activity在隐藏NavigationBar之后仍然可以得到点击事件，不然toggle(long)方法还是无法正常工作。
 * 
 * 3.FLAG_IMMERSIVE和FLAG_IMMERSIVE_STICKY同时使用时，FLAG_IMMERSIVE_STICKY会优先生效。
 * 
 * 4.FLAG_LAYOUT_IN_SCREEN无法让Activity占用ActionBar的布局空间。
 * 若要实现此效果，请在主题中设置<item name="android:windowActionBarOverlay">true</item>。
 * 如果使用的是SupportV7中的ActionBar，请在主题中设置<item name="windowActionBarOverlay">true</item>。
 * 详情参阅：https://developer.android.com/training/basics/actionbar/overlaying.html
 * 
 * @author 王超
 *
 */
public abstract class UtilFullscreen {
	/** 让Activity占用StatusBar和NavigationBar的布局空间。支持V1至V19。 */
	public static final int FLAG_LAYOUT_IN_SCREEN = 0x1;
	/** 隐藏StatusBar。支持V1至V19。
	 * 在V1至V17上，是通过给Activity的Window对象设置参数来实现的；
	 * 从V18开始，是通过View.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN)来实现的。 */
	public static final int FLAG_HIDE_STATUS_BAR = 0x2;
	/** 隐藏ActionBar。支持V1至V19，以及SupportV7中的ActionBar。 */
	public static final int FLAG_HIDE_ACTION_BAR = 0x4;
	/** 淡化StatusBar和NavigationBar。支持V14至V19，在小于V14的版本上，这个Flag没有任何效果。 */
	public static final int FLAG_DIM_SYSTEM_BAR = 0x8;
	/** 隐藏NavigationBar。支持V14至V18，在其它版本上，这个Flag没有任何效果。
	 * 因为在V19之前，隐藏NavigationBar之后，Activity就无法得到点击事件，所以一般不应该使用这个Flag。 */
	public static final int FLAG_HIDE_NAVIGATION_BAR = 0x10;
	/** 隐藏NavigationBar。支持V19，在小于V19的版本上，这个Flag没有任何效果。
	 * 这个Flag一般应该和下面2个Flag一起使用，以便让Activity在隐藏NavigationBar之后仍然可以得到点击事件。 */
	public static final int FLAG_HIDE_NAVIGATION_BAR_V19 = 0x20;
	/** 让Activity在隐藏NavigationBar之后仍然可以得到点击事件。支持V19，在其它版本中，这个Flag没有任何效果。
	 * 这个Flag应该和FLAG_HIDE_NAVIGATION_BAR_V19一起使用，否则也没有效果。 */
	public static final int FLAG_IMMERSIVE = 0x40;
	/** 让Activity在隐藏NavigationBar之后仍然可以得到点击事件。支持V19，在其它版本中，这个Flag没有任何效果。
	 * 这个Flag应该和FLAG_HIDE_STATUS_BAR、FLAG_HIDE_NAVIGATION_BAR_V19一起使用，否则也没有效果。 */
	public static final int FLAG_IMMERSIVE_STICKY = 0x80;

	protected Activity mActivity;
	protected int mFlags;

	protected boolean mVisible;
	protected Handler mMainHandler;
	protected Runnable mHideRunnable;
	protected OnVisibilityChangeListener mOnVisibilityChangeListener;

	private UtilFullscreen(Activity pActivity, int pFlags) {
		mActivity = pActivity;
		mFlags = pFlags;
		mVisible = true;
		mMainHandler = new Handler(Looper.getMainLooper());
		mHideRunnable = new Runnable() {
			@Override
			public void run() {
				hide();
			}
		};
		mOnVisibilityChangeListener = new OnVisibilityChangeListener() {
			@Override
			public void onVisibilityChange(boolean pVisible) {
			}
		};
	}

	public static UtilFullscreen getInstance(Activity pActivity, int pFlags) {
		if (Build.VERSION.SDK_INT < 14) {
			return new UtilFullscreenV1(pActivity, pFlags);
		} else if (Build.VERSION.SDK_INT < 16) {
			return new UtilFullscreenV14(pActivity, pFlags);
		} else if (Build.VERSION.SDK_INT < 18) {
			return new UtilFullscreenV16(pActivity, pFlags);
		} else if (Build.VERSION.SDK_INT < 19) {
			return new UtilFullscreenV18(pActivity, pFlags);
		} else {
			return new UtilFullscreenV19(pActivity, pFlags);
		}
	}

	public abstract void setup();

	public abstract void hide();

	public abstract void show();

	public boolean isVisible() {
		return mVisible;
	}

	public void show(long pDelayMillis) {
		if (!isVisible()) {
			show();
		}
		hide(pDelayMillis);
	}

	public void hide(long pDelayMillis) {
		mMainHandler.removeCallbacks(mHideRunnable);
		mMainHandler.postDelayed(mHideRunnable, pDelayMillis);
	}

	public void toggle() {
		if (isVisible()) {
			hide();
		} else {
			show();
		}
	}

	public void toggle(long pDelayMillis) {
		if (isVisible()) {
			hide(0);
		} else {
			show(pDelayMillis);
		}
	}

	public void setOnVisibilityChangeListener(OnVisibilityChangeListener pOnVisibilityChangeListener) {
		if (pOnVisibilityChangeListener != null) {
			mOnVisibilityChangeListener = pOnVisibilityChangeListener;
		}
	}

	protected boolean checkFlag(int pFlagToCheck) {
		if ((mFlags & pFlagToCheck) != 0) {
			return true;
		} else {
			return false;
		}
	}

	public static interface OnVisibilityChangeListener {
		public void onVisibilityChange(boolean pVisible);
	}

	private static class UtilFullscreenV1 extends UtilFullscreen {
		private UtilFullscreenV1(Activity pActivity, int pFlags) {
			super(pActivity, pFlags);
		}

		@Override
		public void setup() {
			if (checkFlag(FLAG_LAYOUT_IN_SCREEN)) {
				makeContentBehindStatusBar();
			}
		}

		@Override
		public void hide() {
			if (checkFlag(FLAG_HIDE_STATUS_BAR)) {
				hideStatusBar();
			}
			if (checkFlag(FLAG_HIDE_ACTION_BAR)) {
				hideActionBar();
			}

			mVisible = false;
			mOnVisibilityChangeListener.onVisibilityChange(mVisible);
		}

		@Override
		public void show() {
			if (checkFlag(FLAG_HIDE_STATUS_BAR)) {
				showStatusBar();
			}
			if (checkFlag(FLAG_HIDE_ACTION_BAR)) {
				showActionBar();
			}

			mVisible = true;
			mOnVisibilityChangeListener.onVisibilityChange(mVisible);
		}

		protected void makeContentBehindStatusBar() {
			mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
			mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
		}

		protected void hideStatusBar() {
			mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		}

		protected void showStatusBar() {
			mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		}

		protected void hideActionBar() {
			Method _GetActionBarMethod = getGetActionBarMethod();
			
			if (_GetActionBarMethod != null) {
				try {
					Object _ActionBar = _GetActionBarMethod.invoke(mActivity);
					Method _Hide = _ActionBar.getClass().getMethod("hide");
					_Hide.invoke(_ActionBar);
				} catch (Exception e) {
					if (BuildConfig.DEBUG) {
						Log.e(getClass().getName(), "Error when try to hide ActionBar", e);
					}
				}
			}
		}

		protected void showActionBar() {
			Method _GetActionBarMethod = getGetActionBarMethod();
			
			if (_GetActionBarMethod != null) {
				try {
					Object _ActionBar = _GetActionBarMethod.invoke(mActivity);
					Method _Show = _ActionBar.getClass().getMethod("show");
					_Show.invoke(_ActionBar);
				} catch (Exception e) {
					if (BuildConfig.DEBUG) {
						Log.e(getClass().getName(), "Error when try to show ActionBar", e);
					}
				}
			}
		}
		
		private Method getGetActionBarMethod() {
			Method _GetActionBarMethod;
			try {
				_GetActionBarMethod = mActivity.getClass().getMethod("getSupportActionBar");
			} catch (NoSuchMethodException e) {
				try {
					_GetActionBarMethod = mActivity.getClass().getMethod("getActionBar");
				} catch (NoSuchMethodException e1) {
					_GetActionBarMethod = null;
					if (BuildConfig.DEBUG) {
						Log.e(getClass().getName(), "Error when try to get \"getActionBar()\" method", new Throwable(e.getMessage() + "\n" + e1.getMessage()));
					}
				}
			}
			return _GetActionBarMethod;
		}
	}

	@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
	private static class UtilFullscreenV14 extends UtilFullscreenV1 implements View.OnSystemUiVisibilityChangeListener {
		protected int mHideFlags;
		protected int mTestFlags;
		protected int mShowFlags;

		private UtilFullscreenV14(Activity pActivity, int pFlags) {
			super(pActivity, pFlags);
		}

		@Override
		public void setup() {
			super.setup();
			mActivity.getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);

			mHideFlags = View.SYSTEM_UI_FLAG_VISIBLE;
			mTestFlags = View.SYSTEM_UI_FLAG_VISIBLE;
			mShowFlags = View.SYSTEM_UI_FLAG_VISIBLE;

			if (checkFlag(FLAG_HIDE_NAVIGATION_BAR)) {
				addHideNavigationBarFlag();
			}

			if (checkFlag(FLAG_DIM_SYSTEM_BAR)) {
				addLowProfileFlag();
			}
		}

		@Override
		public void hide() {
			if (mHideFlags == View.SYSTEM_UI_FLAG_VISIBLE) {
				super.hide();
			} else {
				mActivity.getWindow().getDecorView().setSystemUiVisibility(mHideFlags);
				if (mTestFlags == View.SYSTEM_UI_FLAG_VISIBLE) {
					super.hide();
				}
			}
		}

		@Override
		public void show() {
			if (mHideFlags == View.SYSTEM_UI_FLAG_VISIBLE) {
				super.show();
			} else {
				mActivity.getWindow().getDecorView().setSystemUiVisibility(mShowFlags);
				if (mTestFlags == View.SYSTEM_UI_FLAG_VISIBLE) {
					super.show();
				}
			}
		}

		/**
		 * 在Android4.0（V14）和Android4.0.3（V15）上，
		 * 如果隐藏了NavigationBar，用户触摸屏幕时，系统会自动显示NavigationBar，
		 * 此时本方法会被回调3次，并且(pVisibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0。
		 * 
		 * 在Android4.1.2（V16）+上，
		 * 如果隐藏了NavigationBar，用户触摸屏幕时，系统会自动显示NavigationBar和StatusBar，
		 * 此时本方法会被回调1次，并且pVisibility == 0。
		 */
		@Override
		public void onSystemUiVisibilityChange(int pVisibility) {
			if (pVisibility == mTestFlags ) {
				super.hide();
			} else if (pVisibility == View.SYSTEM_UI_FLAG_VISIBLE) {
				super.show();
			} else {
				mActivity.getWindow().getDecorView().setSystemUiVisibility(mShowFlags);
			}
		}
		
		protected void addHideNavigationBarFlag() {
			mHideFlags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
			mTestFlags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
		}
		
		protected void clearHideNavigationBarFlag() {
			mHideFlags &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
			mTestFlags &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
		}

		protected void addLowProfileFlag() {
			mHideFlags |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
			mTestFlags |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
		}
		
		protected void clearLowProfileFlag() {
			mHideFlags &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
			mTestFlags &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
		}
	}

	@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
	private static class UtilFullscreenV16 extends UtilFullscreenV14 {

		private UtilFullscreenV16(Activity pActivity, int pFlags) {
			super(pActivity, pFlags);
		}

		@Override
		public void setup() {
			super.setup();

			if (checkFlag(FLAG_LAYOUT_IN_SCREEN) && checkFlag(FLAG_HIDE_NAVIGATION_BAR)) {
				addLayoutHideNavigationFlag();
			}
		}

		protected void addLayoutHideNavigationFlag() {
			mHideFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
			mShowFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
		}
		
		protected void clearLayoutHideNavigationFlag() {
			mHideFlags &= ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
			mShowFlags &= ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
		}
	}

	
	@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
	private static class UtilFullscreenV18 extends UtilFullscreenV16 {
		private UtilFullscreenV18(Activity pActivity, int pFlags) {
			super(pActivity, pFlags);
		}

		@Override
		public void setup() {
			super.setup();
			
			if (checkFlag(FLAG_HIDE_STATUS_BAR)) {
				addFullscreenFlag();
			}
			
			if (checkFlag(FLAG_LAYOUT_IN_SCREEN)) {
				addLayoutFullscreenFlag();
			}
		}

		protected void addFullscreenFlag() {
			mHideFlags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
			mTestFlags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
		}
		
		protected void clearFullscreenFlag() {
			mHideFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
			mTestFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
		}
		
		protected void addLayoutFullscreenFlag() {
			mHideFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
			mShowFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
		}
		
		protected void clearLayoutFullscreenFlag() {
			mHideFlags &= ~View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
			mShowFlags &= ~View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
		}
		
		/**
		 * 因为从V18开始，使用了新方法让Activity占用StatusBar的布局空间，所以重写本方法，让其不再执行任何代码。
		 */
		@Override
		protected void makeContentBehindStatusBar() {
		}
		
		/**
		 * 因为从V18开始，使用了新方法隐藏StatusBar，所以重写本方法，让其不再执行任何代码。
		 */
		@Override
		protected void hideStatusBar() {
		}
		
		/**
		 * 因为从V18开始，使用了新方法显示StatusBar，所以重写本方法，让其不再执行任何代码。
		 */
		@Override
		protected void showStatusBar() {
		}
	}
	
	@TargetApi(Build.VERSION_CODES.KITKAT)
	private static class UtilFullscreenV19 extends UtilFullscreenV18 {
		private UtilFullscreenV19(Activity pActivity, int pFlags) {
			super(pActivity, pFlags);
		}
		
		@Override
		public void setup() {
			super.setup();
			
			if (checkFlag(FLAG_LAYOUT_IN_SCREEN) && checkFlag(FLAG_HIDE_NAVIGATION_BAR_V19)) {
				addLayoutHideNavigationFlagV19();
			}
			
			if (checkFlag(FLAG_HIDE_NAVIGATION_BAR_V19)) {
				addHideNavigationBarFlagV19();
			}
			
			if (checkFlag(FLAG_IMMERSIVE)) {
				setImmersiveFlag();
			}
			
			if (checkFlag(FLAG_IMMERSIVE_STICKY)) {
				setImmersiveStickyFlag();
				/** 如果SYSTEM_UI_FLAG_IMMERSIVE和SYSTEM_UI_FLAG_LOW_PROFILE一起用的话，
				 * 在临时显示系统栏时，会导致回调onSystemUiVisibilityChange()方法。 */
				clearLowProfileFlag();
			}
		}
		
		protected void addHideNavigationBarFlagV19() {
			super.addHideNavigationBarFlag();
		}
		
		protected void clearHideNavigationBarFlagV19() {
			super.clearHideNavigationBarFlag();
		}
		
		protected void addLayoutHideNavigationFlagV19() {
			super.addLayoutHideNavigationFlag();
		}
		
		protected void clearLayoutHideNavigationFlagV19() {
			super.clearLayoutHideNavigationFlag();
		}
		
		protected void setImmersiveFlag() {
			mHideFlags |= View.SYSTEM_UI_FLAG_IMMERSIVE;
		}
		
		protected void clearImmersiveFlag() {
			mHideFlags &= ~View.SYSTEM_UI_FLAG_IMMERSIVE;
		}
		
		protected void setImmersiveStickyFlag() {
			mHideFlags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
		}
		
		protected void clearImmersiveStickyFlag() {
			mHideFlags &= ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
		}

		/**
		 * 从V19开始，使用FLAG_HIDE_NAVIGATION_BAR_V19判断是否隐藏状态栏，所以重写本方法，让其不执行任何代码。
		 */
		@Override
		protected void addHideNavigationBarFlag() {
		}

		/**
		 * 从V19开始，使用FLAG_HIDE_NAVIGATION_BAR_V19判断是否隐藏状态栏，所以重写本方法，让其不执行任何代码。
		 */
		@Override
		protected void clearHideNavigationBarFlag() {
		}

		/**
		 * 从V19开始，使用FLAG_HIDE_NAVIGATION_BAR_V19判断是否隐藏状态栏，所以重写本方法，让其不执行任何代码。
		 */
		@Override
		protected void addLayoutHideNavigationFlag() {
		}

		/**
		 * 从V19开始，使用FLAG_HIDE_NAVIGATION_BAR_V19判断是否隐藏状态栏，所以重写本方法，让其不执行任何代码。
		 */
		@Override
		protected void clearLayoutHideNavigationFlag() {
		}
		
	}

}